#ifndef __VECTOR3_H__
#define __VECTOR3_H__

#include <assert.h>
#include <math.h>

#if defined(__PPU__)
#if defined(__PS3__)
#include <altivec.h>
// damn bastards redefine bool as vector type
#undef bool
typedef vector float HwVector;
#else
typedef vector float HwVector;
#endif
#elif defined(__SPU__)
typedef vector float HwVector;
#else
#define __X86__
#include <xmmintrin.h>
typedef __m128 HwVector;
#endif

struct Vector3 {
    explicit inline Vector3() {}
    inline Vector3(const float _x, const float _y, const float _z) {
        hwVector = (HwVector) {_x, _y, _z, 0.0f};
    }
    inline Vector3(const Vector3& srcVector) {
        hwVector = srcVector.hwVector;
    }

    // unary operators
    inline Vector3 operator+() const {
        return *this;
    }
    inline Vector3 operator-() const {
        Vector3 result;
#ifdef __X86__
        result.hwVector = _mm_sub_ps(_mm_set_ps1(0), hwVector);
#else
        result.hwVector = -hwVector;
#endif
        return result;
    }

    // assignment operators
    inline void operator=(const Vector3& v) {
        hwVector = v.hwVector;
    }
    inline void operator+=(const Vector3& v) {
#ifdef __X86__
        hwVector = _mm_add_ps(hwVector, v.hwVector);
#else
        hwVector += v.hwVector;
#endif
    }
    inline void operator-=(const Vector3& v) {
#ifdef __X86__
        hwVector = _mm_sub_ps(hwVector, v.hwVector);
#else
        hwVector -= v.hwVector;
#endif
    }
    inline void operator*=(const float srcFloat);
    inline void operator/=(const float srcFloat);

    // methods
    inline float length_2() const {
        return x*x + y*y + z*z; // TODO: use vector math if needed
    }
    inline float length() const {
        return sqrtf(length_2());
    }

    union {
        struct {
            float x;
            float y;
            float z;
            float _pad;
        };
        float array[3];
        HwVector hwVector;
    };
};

// external operators
inline Vector3 operator+(const Vector3& v1, const Vector3& v2) {
    Vector3 result;
#ifdef __X86__
    result.hwVector = _mm_add_ps(v1.hwVector, v2.hwVector);
#else
    result.hwVector = v1.hwVector + v2.hwVector;
#endif
    return result;
}
inline Vector3 operator-(const Vector3& v1, const Vector3& v2) {
    Vector3 result;
#ifdef __X86__
    result.hwVector = _mm_sub_ps(v1.hwVector, v2.hwVector);
#else
    result.hwVector = v1.hwVector - v2.hwVector;
#endif
    return result;
}
inline Vector3 operator*(const Vector3& v, const float srcFloat) {
    Vector3 result;
#ifdef __X86__
    result.hwVector = _mm_mul_ps(v.hwVector, _mm_set_ps1(srcFloat));
#else
    result.hwVector = v.hwVector*(HwVector){srcFloat, srcFloat, srcFloat, srcFloat};
#endif
    return result;
}
inline Vector3 operator*(const float srcFloat, const Vector3& v) {
    return v*srcFloat;
}
inline Vector3 operator/(const Vector3& v, const float srcFloat) {
    assert(srcFloat != 0);
    const float inverseFloat = 1.0f/srcFloat;
    return v*inverseFloat;
}

// implementations of forwards
inline void Vector3::operator*=(const float srcFloat) {
#ifdef __X86__
    hwVector = _mm_mul_ps(hwVector, _mm_set_ps1(srcFloat));
#else
    (*this) = (*this)*srcFloat;
#endif
}
inline void Vector3::operator/=(const float srcFloat) {
    assert(srcFloat != 0.0f);
    const float inverseFloat = 1.0f/srcFloat;
#ifdef __X86__
    hwVector = _mm_mul_ps(hwVector, _mm_set_ps1(inverseFloat));
#else
    (*this) = (*this)*inverseFloat;
#endif
}

// strict compare operators
inline bool operator==(const Vector3& v1, const Vector3& v2) {
    return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z);
}
inline bool operator!=(const Vector3& v1, const Vector3& v2) {
    return (v1.x != v2.x) || (v1.y != v2.y) || (v1.z != v2.z);
}

// functions
inline float dot(const Vector3& v1, const Vector3& v2) {
    return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; // TODO: use vector math if needed
}
inline Vector3 cross(const Vector3& v1, const Vector3& v2) {
    return Vector3(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); // TODO: use vector math if needed
}

#endif // __VECTOR3_H__
